#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QDir>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::imgShowGray(const Mat &img)
{
    QImage showImg = QImage((const unsigned char*)(img.data), img.cols, img.rows, img.step, QImage::Format_Grayscale8);//Format_RGB888
    ui->viewLabel->setPixmap(QPixmap::fromImage(showImg.scaled(ui->viewLabel->size(), Qt::KeepAspectRatio)));//在Qt界面上显示图像
}

void MainWindow::imgShowRGB(const Mat &img)
{
    QImage showImg = QImage((const unsigned char*)(img.data), img.cols, img.rows, img.step, QImage::Format_BGR888);//Format_RGB888
    ui->viewLabel->setPixmap(QPixmap::fromImage(showImg.scaled(ui->viewLabel->size(), Qt::KeepAspectRatio)));//在Qt界面上显示图像
}

void MainWindow::on_pushButton_imgCSV_clicked()
{
    // 打开文件选择对话框以选择文件夹
    selectedFolder = QFileDialog::getExistingDirectory(this, "选择一个路径", QDir::currentPath());
    // 检查用户是否选择了文件夹
    if(selectedFolder.isEmpty()){
        qWarning()<<"没有选择有效的文件夹";
        return ;
    }
    qDebug()<<"selectedFolder"<<selectedFolder; // 指定路径

    QFile file(selectedFolder+"/faces.csv");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)){
        qWarning()<<"打开文件失败";
        return ;
    }
    // 创建一个QDir对象以表示所选文件夹
    QDir selectedDir(selectedFolder);
    // 获取所选文件夹的路径
    QStringList folderNames = selectedDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
    // 输出所选文件夹的名称
    for (const QString &folderName : folderNames) {
        //qDebug() << "Folder name:" << folderName;
        //int label = folderName.toInt();
        QString subPath = selectedFolder+"/"+folderName;
        QDir dir(subPath);
        // 获取所有文件和文件夹
        QStringList entries = dir.entryList(QDir::Files);
        for (const QString &imgPath : entries) {
            QString csvAbsolutePath = subPath+"/"+imgPath+","+folderName;
            qDebug().noquote()<<csvAbsolutePath;
            // 写入文本
            QTextStream(&file) << csvAbsolutePath+QString("\n");
        }
    }
    // 关闭文件
    file.close();
}

void MainWindow::on_detectPushButton_clicked()
{
    // 如果用户没有选择路径，直接要求用户选择csv文件
    QString csvFileName;
    if(selectedFolder.isEmpty()){
        csvFileName = QFileDialog::getOpenFileName(this,"图片信息文件","./","csv文件(*.csv);");

        //QDir csvDir(csvFileName);
        QString absoluteFilePath = QFileInfo(csvFileName).path();
        if(absoluteFilePath.isEmpty()){
            qWarning()<<"没有选择有效的csv文件";
            return;
        }
        selectedFolder = absoluteFilePath;
    }
    qDebug()<<"selectedFolder"<<selectedFolder;
    // 检查文件夹是否存在
    if (!QDir(selectedFolder+"/standardFace").exists()) {
        // 如果不存在，则创建文件夹
        if (!QDir().mkpath(selectedFolder+"/standardFace")) {
            qWarning() << "文件夹创建失败！";
            return ;
        }
    }
    //打开csv文件
    QFile csvfile(csvFileName);
    if (!csvfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "打开文件失败";
        return ;
    }
    img_IDPathList.clear();
    //按行读取csv文件
    QTextStream stream(&csvfile);
    while (!stream.atEnd()) {
        QString lineText = stream.readLine();
        QStringList id_paht = lineText.split(',');
        if(id_paht.count() >= 2){
            myKeyValue keyval;
            keyval.key = id_paht.at(1).toInt();
            keyval.value = id_paht.at(0);
            img_IDPathList.append(keyval);
        }
    }
    csvfile.close();

    //创建分类器
    CascadeClassifier classifier;//分类器
    // 加载分类器模板
    string fDetectorPath = "haarcascade_frontalface_alt.xml";
    if(!classifier.load(fDetectorPath)){
        qWarning()<<"加载分类器错误";
        return;
    }
    // 加载人眼模板
    CascadeClassifier eyes_detector;
    string eDetectorPath = "haarcascade_eye_tree_eyeglasses.xml";
    if(!eyes_detector.load(eDetectorPath)){
        qWarning()<<"加载分类器错误";
        return;
    }

    //foreach(QString path, img_IDPathList.values()){
    //QStringList imgPathList = img_IDPathList.values();
    qDebug()<<"imgPathList"<<img_IDPathList.count();
    for(int m=0; m<img_IDPathList.count(); m++){
        QString path = img_IDPathList.at(m).value;
        qDebug()<<"path"<<path;
        //读取图像数据
        Mat imgData = imread(path.toLatin1().data());
        if(imgData.empty()){
            qWarning()<<"图片路径错误";
            return;
        }
        //图像格式转换(避免图像颜色失真)
        Mat imgGray;
        cvtColor(imgData, imgGray, COLOR_BGR2GRAY); //COLOR_BGR2RGB
#if 0
        //双边滤波
        Mat imgDst4 = imgGray.clone();
        for (int i = 1; i < 30; i = i + 2)
            bilateralFilter(imgGray, imgDst4, i, i * 2, i / 2);
        //二值化
        threshold(imgGray, imgGray, 150, 255, THRESH_BINARY);
#endif
        // 图像直方图，增强特征
        equalizeHist(imgGray, imgGray);
        // 人脸识别
        vector<Rect> faces;
        classifier.detectMultiScale(imgGray, faces, 1.2, 5, CV_HAAR_SCALE_IMAGE, Size(30, 30));//-- 多尺寸检测人脸
        // 人脸识别结果
        for (quint64 i = 0; i < faces.size(); i++) {
            // 复制人脸
            Mat face;
            imgGray(faces.at(i)).copyTo(face);
            // 在人脸范围内 识别人眼
            vector<Rect> eyes;
            eyes_detector.detectMultiScale(face, eyes, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));//-- 在每张人脸上检测双眼
            // 如果在人脸范围内识别到至少一个眼睛，可以认为是合格人脸

            // 将裁剪出来的人脸，标准大小128*128
            Mat standardFace;
            cv::resize(face, standardFace, cv::Size(128, 128));

            QString imgPath = selectedFolder+"/standardFace/"+QFileInfo(path).fileName();
            qDebug()<<"imgPath"<<imgPath;
            cv::imwrite(imgPath.toLatin1().data(),standardFace);
            //画出人脸的矩形区
            rectangle(imgData, faces.at(i),Scalar(255,255,255),3);
            //画出人眼区域
            for (quint64 e = 0; e < eyes.size(); e++)
            {
                Point center(faces[i].x + eyes[e].x + eyes[e].width * 0.5, faces[i].y + eyes[e].y + eyes[e].height * 0.5);
                int radius = cvRound((eyes[e].width + eyes[e].height) * 0.25);
                circle(imgData, center, radius, Scalar(255, 0, 0), 2, 8, 0);
            }
            imgShowRGB(imgData);
            if(eyes.size() > 0){

            }else{
                qWarning()<<path<<"不是合格人脸";
            }
        }
    }
}

void MainWindow::on_detectPushButton_train_clicked()
{
    QString standardImg;
    if(selectedFolder.isEmpty()){
        standardImg = QFileDialog::getExistingDirectory(this, "标准脸路径", QDir::currentPath());
        if(standardImg.isEmpty()){
            qWarning()<<"打开路径失败";
            return;
        }
        selectedFolder = standardImg;
    }

    QDir imgDir(standardImg);
    QStringList imgNames = imgDir.entryList(QDir::Files);
    qDebug()<<"imgNames"<<imgNames;

    if(imgNames.count() < 1){
        qDebug()<<"没有发现标准人脸模型";
        return ;
    }
    // 清除所有数据
    //images.clear();
    //labels.clear();

    for(int i=0;i<imgNames.count();i++){
        QString imgPath = standardImg+"/"+imgNames.at(i);
        //读取图像数据
        Mat imgData = imread(imgPath.toLatin1().data(),0);
        if(imgData.empty()){
            qWarning()<<"打开图片错误";
            return;
        }
        int faceId = 0;
        if(imgNames.at(i).contains('a')) faceId = 1;
        if(imgNames.at(i).contains('b')) faceId = 2;
        if(imgNames.at(i).contains('c')) faceId = 3;
        if(imgNames.at(i).contains('d')) faceId = 4;
        images.push_back(imgData);
        labels.push_back(faceId);
        qDebug()<<"faceId->imgPath"<<faceId<<":"<<imgPath;
    }

    //1.训练
    //Ptr<cv::face::BasicFaceRecognizer>
    model = cv::face::EigenFaceRecognizer::create();
    model->train(images,labels);
#if 0
    //2.预测
    int predictLabel = model->predict(testImage);
    //此处如果样本和预测结果是一致的就说明此次识别是算法是成功的

    if(predictLabel == testLable){
        qDebug()<<"训练结果符合预期";
    }else{
        qDebug()<<"testLable"<<testLable;
        qDebug()<<"predictLabel"<<predictLabel;
    }

    //从训练结果中获取均值、特征向量、特征值矩阵
    Mat eigenvalues = model->getEigenValues();
    Mat eigenvectors = model->getEigenVectors();
    Mat mean = model->getMean();
    //得到均值脸
    Mat meanFace = mean.reshape(1,height);
    Mat dst;
    //归一化0~255并输出
    if(meanFace.channels()==1){//单通道图像
        normalize(meanFace,dst,0,255,NORM_MINMAX,CV_8UC1);
    }else{//多通道图像
        normalize(meanFace,dst,0,255,NORM_MINMAX,CV_8UC3);
    }
    //imshow("dist",dst);
#endif
    //保持结果
    model->write("face.xml");
}

//
void MainWindow::on_detectPushButton_test_clicked()
{
    //加载一个人脸识别器
    Ptr<face::BasicFaceRecognizer> model_test = face::EigenFaceRecognizer::create();
    //opencv3.3要用read，要不然会出错
    model_test->read("face.xml");

    QString fileName = QFileDialog::getOpenFileName(this,"打开图片","./","图片文件(*jpg);");
    qDebug()<<"fileName"<<fileName;
    Mat imgData = imread(fileName.toLatin1().data()); //读取图像数据
    Mat imgGray;
    cvtColor(imgData, imgGray, COLOR_BGR2GRAY); //COLOR_BGR2RGB //图像格式转换(避免图像颜色失真)
    equalizeHist(imgGray, imgGray);
    //创建分类器
    CascadeClassifier classifier;//分类器
    //加载分类器模板
    string fDetectorPath = "haarcascade_frontalface_alt.xml";
    classifier.load(fDetectorPath);

    vector<Rect> objs;
    //classifier.detectMultiScale(imgGray, objs, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));//-- 多尺寸检测人脸
    classifier.detectMultiScale(imgGray, objs, 1.1, 2, 0, Size(30, 30), Size(380, 400));
    for (quint64 i = 0; i < objs.size(); i++)
    {
        Mat face;
        imgGray(objs.at(i)).copyTo(face);
        Mat standardFace;
        cv::resize(face, standardFace, cv::Size(128, 128));

        int faceid = model_test->predict(standardFace);
        QString name;
        switch (faceid) {
        case 1: name = "zhaobenshan"; break;
        case 2: name = "fengxiaogang"; break;
        case 3: name = "kanghui"; break;
        case 4: name = "Leonardo"; break;
        default: name = "unknow"; break;
        }
        qDebug()<<"name"<<name;
        putText(imgGray, name.toUtf8().data(), objs.at(i).tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 2, 8);
        rectangle(imgGray, objs.at(i),Scalar(255,255,255),3);
    }
    imgShowGray(imgGray);
}





















